package com.nbsaas.boot.plugin;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Collectors;

/**
 * 插件管理器
 * 负责插件的加载、启动和停止
 */
public class PluginManager {
    private final Map<String, Plugin> plugins = new ConcurrentHashMap<>();
    private final Map<String, URLClassLoader> classLoaders = new ConcurrentHashMap<>();
    private final PluginContext context;
    
    public PluginManager() {
        this.context = new PluginContext(this);
    }
    
    /**
     * 加载插件
     * @param pluginJar 插件JAR文件
     * @return 加载的插件实例
     * @throws IOException 如果加载失败
     */
    public Plugin loadPlugin(File pluginJar) throws IOException {
        try (JarFile jar = new JarFile(pluginJar)) {
            // 查找插件主类
            String mainClass = findMainClass(jar);
            if (mainClass == null) {
                throw new IOException("找不到插件主类");
            }
            
            // 创建类加载器
            URLClassLoader classLoader = new URLClassLoader(
                new URL[]{pluginJar.toURI().toURL()},
                getClass().getClassLoader()
            );
            
            // 加载插件类
            Class<?> pluginClass = classLoader.loadClass(mainClass);
            if (!Plugin.class.isAssignableFrom(pluginClass)) {
                throw new IOException("插件类必须实现Plugin接口");
            }
            
            // 实例化插件
            Plugin plugin = (Plugin) pluginClass.getDeclaredConstructor().newInstance();
            String pluginId = plugin.getId();
            
            // 检查插件ID是否重复
            if (plugins.containsKey(pluginId)) {
                throw new IOException("插件ID重复: " + pluginId);
            }
            
            // 保存插件实例和类加载器
            plugins.put(pluginId, plugin);
            classLoaders.put(pluginId, classLoader);
            
            return plugin;
        } catch (Exception e) {
            throw new IOException("加载插件失败", e);
        }
    }
    
    /**
     * 启动插件
     * @param pluginId 插件ID
     * @throws IllegalStateException 如果插件未加载或已启动
     */
    public void startPlugin(String pluginId) {
        Plugin plugin = plugins.get(pluginId);
        if (plugin == null) {
            throw new IllegalStateException("插件未加载: " + pluginId);
        }
        
        plugin.init(context);
        plugin.start();
    }
    
    /**
     * 停止插件
     * @param pluginId 插件ID
     * @throws IllegalStateException 如果插件未加载
     */
    public void stopPlugin(String pluginId) {
        Plugin plugin = plugins.get(pluginId);
        if (plugin == null) {
            throw new IllegalStateException("插件未加载: " + pluginId);
        }
        
        plugin.stop();
    }
    
    /**
     * 卸载插件
     * @param pluginId 插件ID
     * @throws IllegalStateException 如果插件未加载
     */
    public void unloadPlugin(String pluginId) {
        Plugin plugin = plugins.remove(pluginId);
        if (plugin == null) {
            throw new IllegalStateException("插件未加载: " + pluginId);
        }
        
        URLClassLoader classLoader = classLoaders.remove(pluginId);
        if (classLoader != null) {
            try {
                classLoader.close();
            } catch (IOException e) {
                // 忽略关闭异常
            }
        }
    }
    
    /**
     * 获取所有已加载的插件
     * @return 插件列表
     */
    public List<Plugin> getPlugins() {
        return new ArrayList<>(plugins.values());
    }
    
    /**
     * 获取插件
     * @param pluginId 插件ID
     * @return 插件实例，如果未加载则返回null
     */
    public Plugin getPlugin(String pluginId) {
        return plugins.get(pluginId);
    }
    
    private String findMainClass(JarFile jar) throws IOException {
        // 首先检查MANIFEST.MF
        String mainClass = jar.getManifest().getMainAttributes().getValue("Plugin-Main-Class");
        if (mainClass != null) {
            return mainClass;
        }
        
        // 如果没有指定主类，查找实现Plugin接口的类
        return jar.stream()
            .filter(entry -> entry.getName().endsWith(".class"))
            .map(JarEntry::getName)
            .map(name -> name.replace('/', '.').replace(".class", ""))
            .filter(className -> {
                try {
                    Class<?> clazz = Class.forName(className);
                    return Plugin.class.isAssignableFrom(clazz);
                } catch (ClassNotFoundException e) {
                    return false;
                }
            })
            .findFirst()
            .orElse(null);
    }
} 